#pragma once
#include<brpc/channel.h>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<mutex>
#include"logger.hpp"
//这个文件是对brpc的channel的封装
//结构为一个项目->多个服务，一个服务->多个可提供服务的服务url
//实现通过传参[服务名字-服务url]上线和下线服务
//通过服务名字即可访问channel，通过channel调用访问服务
namespace zzy_mims
{
class ServiceChannels
{
public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    ServiceChannels(const std::string& service_name)
    :_service_name(service_name),_index(-1)
    {};
    void append (const std::string& host)
    {
        ChannelPtr channel(new brpc::Channel);
        brpc::ChannelOptions options;
        options.connect_timeout_ms = -1; // 配置连接等待超时时间
        options.timeout_ms = -1;         // 配置rpc请求等待时间
        options.max_retry = 3;           // 请求重试次数
        options.protocol = "baidu_std";  // 序列化协议，默认使用baidu_std
        int ret = channel->Init(host.c_str(), &options);
        if (ret == -1)
        {
            LOG_ERROR("初始化{}-{}信道失败\n",_service_name,host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _Channels.emplace_back(host,channel);
    }
    void remove(const std::string& host)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        for(auto it =_Channels.begin();it!=_Channels.end();it++)
        {
            if(it->first==host)
            {
                _Channels.erase(it);
                return;
            }
        }
        LOG_WARN("需要删除的{}-{}信道未被管理\n",_service_name,host);
        return;
    }
    ChannelPtr choose()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_Channels.size()==0)
        {
            LOG_ERROR("当前没有能够提供{}服务的节点\n",_service_name);
            return ChannelPtr();
        }
        return _Channels[++_index%_Channels.size()].second;
    }
private:
    std::mutex _mutex;
    std::string _service_name;
    //host-channel
    std::vector<std::pair<std::string,ChannelPtr>> _Channels;
    //RR轮转计数器
    int _index;
};
class ServChanManager
{
public:
    using ChannelPtr = ServiceChannels::ChannelPtr;
    using ServicePtr = std::shared_ptr<ServiceChannels>;
    using ptr = std::shared_ptr<ServChanManager>;
    void Declared(const std::string& service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _focused_services.emplace(service_name);
    }
    void ServiceOnline(const std::string& service_instance,const std::string& host)
    {
        std::string service_name = GetServiceName(service_instance);
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_focused_services.count(service_name) == 0)
            {
                LOG_DEBUG("{}-{}服务上线了，但是并不关系\n", service_name, host);
                return;
            }
            if (_services.count(service_name) == 0)
                _services[service_name] = std::make_shared<ServiceChannels>(service_name);
        }//不让append函数的锁和lock锁重叠,防止卡死锁
        _services[service_name]->append(host);
        LOG_DEBUG("关注的{}-{}服务上线,添加管理\n",service_name,host);
    };
    void ServiceOffline(const std::string& service_instance,const std::string& host)
    {
        std::string service_name = GetServiceName(service_instance);
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_focused_services.count(service_name) == 0)
            {
                LOG_DEBUG("{}-{}服务下线了，但是并不关系\n", service_name, host);
                return;
            }
            if (_services.count(service_name) == 0)
            {
                LOG_WARN("需要下线的服务节点{}-{}的管理对象未找到\n",service_name,host);
                return;
            }
        }//不让remove函数的锁和lock锁重叠,防止卡死锁
        _services[service_name]->remove(host);
        LOG_DEBUG("关注的{}-{}服务下线，进行管理删除\n",service_name,host);
        return;
    };
    ChannelPtr choose(const std::string& service_name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_services.count(service_name) == 0)
        {
            LOG_ERROR("当前没有能够提供{}服务的节点\n",service_name);
            return ChannelPtr();
        }
        return _services[service_name]->choose();
    }
private:
    const std::string GetServiceName(const std::string& service_instance)
    {
        auto pos = service_instance.find_last_of('/');
        if(pos==std::string::npos) return service_instance;
        return service_instance.substr(0,pos);
    };
    std::mutex _mutex;
    std::unordered_set<std::string> _focused_services;
    std::unordered_map<std::string,ServicePtr> _services; 
};
}


